package cn.demoncat.util.conn;

import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.PooledObjectFactory;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cn.demoncat.util.exception.ReflexRuntimeException;
import cn.demoncat.util.lang.ParamEmptyUtil;
import cn.demoncat.util.lang.StringUtil;

/**
 * 连接工厂（根类）
 * <br/>
 * 注意：根工厂默认使用反射创建连接，可以自定义子类来重写create，或者自定义连接并编写构造方法Conn(String url, C config)
 * <br/>
 * 泛型：配置ConnPoolConfig，连接Conn
 * 
 * @author 延晓磊
 *
 * @since 2019年11月4日
 */
public class ConnPoolFactory<C extends ConnPoolConfig<T>, T extends Conn> implements PooledObjectFactory<T> {

	// 连接的实际类型（通过默认的create方法反射创建连接对象时必传）
	private final Class<T> clazz;
	// 连接地址（多个地址时随机抽取）
	private final String[] urls;
	// 连接配置
	private final C config;
	
    private static final Logger LOG = LoggerFactory.getLogger(ConnPoolFactory.class);
    
    /**
     * 创建连接工厂（自由实现，子类必须重写create方法）
     * 
     * @param config		连接配置（非空）
     * 
     * @author 延晓磊
     *
     * @since 2019年11月6日
     */
    protected ConnPoolFactory(C config) {
		this(null, config);
	}
    
    /**
     * 创建连接工厂（默认实现）
     * 
     * @param clazz		连接的实际类型（通过默认的create方法反射创建连接对象时必传）
     * @param config	连接配置（非空）
     * 
     * @author 延晓磊
     *
     * @since 2019年11月6日
     */
    public ConnPoolFactory(Class<T> clazz, C config) {
		super();
		// 配置
		ParamEmptyUtil.checkNull(config);
		this.config = config;
		this.urls = StringUtils.isBlank(config.getUrl()) ? null : StringUtil.split(config.getUrl());
		// 连接
		this.clazz = clazz;
	}
    
	/**
	 * 创建连接(通过class反射创建，相关连接必须有对应的构造方法)
	 * 
	 * @param url		连接地址
	 * @param config	连接配置
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年11月4日
	 */
	protected T create(String url, C config) {
		try {
			return this.clazz.getConstructor(String.class, config.getClass()).newInstance(url, config);
		} catch (Exception e) {
			throw new ReflexRuntimeException("创建连接失败", e);
		}
	}
    
    /**
     * 创建连接
     * 
     * @return 连接
     * 
     * @throws Exception
     *
     * @author 延晓磊
     *
     * @since 2019年11月4日
     */
	@Override
	public PooledObject<T> makeObject() throws Exception {
		// 随机抽取
		String url = urls == null ? null : urls.length == 1 ? urls[0] : urls[RandomUtils.nextInt(0, urls.length)];
		LOG.debug("创建连接：url = {}", url);
		return new DefaultPooledObject<>(create(url, config));
	}

	/**
	 * 销毁连接
	 * 
	 * @param p
	 * @throws Exception
	 *
	 * @author 延晓磊
	 *
	 * @since 2019年11月4日
	 *
	 */
	@Override
	public void destroyObject(PooledObject<T> p) throws Exception {
		T o = p.getObject();
		LOG.debug("关闭连接：url = {}", o.getUrl());
		o.close();
	}

	/**
	 * 校验连接是否有效
	 * 
	 * @param p
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2019年11月4日
	 *
	 */
	@Override
	public boolean validateObject(PooledObject<T> p) {
		return p.getObject().isValid();
	}

	/**
	 * 激活连接
	 * 
	 * @param p
	 * @throws Exception
	 *
	 * @author 延晓磊
	 *
	 * @since 2019年11月4日
	 */
	@Override
	public void activateObject(PooledObject<T> p) throws Exception {
		p.getObject().activate();
	}

	/**
	 * 钝化连接
	 * 
	 * @param p
	 * @throws Exception
	 *
	 * @author 延晓磊
	 *
	 * @since 2019年11月4日
	 *
	 */
	@Override
	public void passivateObject(PooledObject<T> p) throws Exception {
		p.getObject().passivate();
	}
 
}
